home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 August (Alt) / CHIP 2005-08.1.iso / program / guvenlik / syslinux-3.07.exe / dos / syslinux.c < prev    next >
Encoding:
C/C++ Source or Header  |  2004-12-28  |  13.2 KB  |  595 lines

  1. #ident "$Id: syslinux.c,v 1.13 2004/12/28 22:51:44 hpa Exp $"
  2. /* ----------------------------------------------------------------------- *
  3.  *   
  4.  *   Copyright 1998-2004 H. Peter Anvin - All Rights Reserved
  5.  *
  6.  *   This program is free software; you can redistribute it and/or modify
  7.  *   it under the terms of the GNU General Public License as published by
  8.  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
  9.  *   Boston MA 02111-1307, USA; either version 2 of the License, or
  10.  *   (at your option) any later version; incorporated herein by reference.
  11.  *
  12.  * ----------------------------------------------------------------------- */
  13.  
  14. /*
  15.  * syslinux.c - Linux installer program for SYSLINUX
  16.  *
  17.  * Hacked up for DOS.
  18.  */
  19.  
  20. #include <errno.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <stdlib.h>
  24. #include "mystuff.h"
  25.  
  26. #include "syslinux.h"
  27. #include "libfat.h"
  28.  
  29. const char *program = "syslinux"; /* Name of program */
  30. uint16_t dos_version;
  31.  
  32. #ifdef DEBUG
  33. # define dprintf printf
  34. #else
  35. # define dprintf(...) ((void)0)
  36. #endif
  37.  
  38. void __attribute__((noreturn)) usage(void)
  39. {
  40.   puts("Usage: syslinux [-sfma] <drive>: [bootsecfile]\n");
  41.   exit(1);
  42. }
  43.  
  44. void unlock_device(int);
  45.  
  46. void __attribute__((noreturn)) die(const char *msg)
  47. {
  48.   unlock_device(0);
  49.   puts("syslinux: ");
  50.   puts(msg);
  51.   putchar('\n');
  52.   exit(1);
  53. }
  54.  
  55. /*
  56.  * read/write wrapper functions
  57.  */
  58. int creat(const char *filename, int mode)
  59. {
  60.   uint16_t rv;
  61.   uint8_t err;
  62.  
  63.   dprintf("creat(\"%s\", 0x%x)\n", filename, mode);
  64.  
  65.   rv = 0x3C00;
  66.   asm volatile("int $0x21 ; setc %0"
  67.            : "=abcdm" (err), "+a" (rv)
  68.            : "c" (mode), "d" (filename));
  69.   if ( err ) {
  70.     dprintf("rv = %d\n", rv);
  71.     die("cannot open ldlinux.sys");
  72.   }
  73.  
  74.   return rv;
  75. }
  76.  
  77. void close(int fd)
  78. {
  79.   uint16_t rv = 0x3E00;
  80.  
  81.   dprintf("close(%d)\n", fd);
  82.  
  83.   asm volatile("int $0x21"
  84.            : "+a" (rv)
  85.            : "b" (fd));
  86.  
  87.   /* The only error MS-DOS returns for close is EBADF,
  88.      and we really don't care... */
  89. }
  90.  
  91. ssize_t write_file(int fd, const void *buf, size_t count)
  92. {
  93.   uint16_t rv;
  94.   ssize_t done = 0;
  95.   uint8_t err;
  96.  
  97.   dprintf("write_file(%d,%p,%u)\n", fd, buf, count);
  98.  
  99.   while ( count ) {
  100.     rv = 0x4000;
  101.     asm volatile("int $0x21 ; setc %0"
  102.          : "=abcdm" (err), "+a" (rv)
  103.          : "b" (fd), "c" (count), "d" (buf));
  104.     if ( err || rv == 0 )
  105.       die("file write error");
  106.  
  107.     done += rv;
  108.     count -= rv;
  109.   }
  110.  
  111.   return done;
  112. }
  113.  
  114. struct diskio {
  115.   uint32_t startsector;
  116.   uint16_t sectors;
  117.   uint16_t bufoffs, bufseg;
  118. } __attribute__((packed));
  119.  
  120. void write_device(int drive, const void *buf, size_t nsecs, unsigned int sector)
  121. {
  122.   uint8_t err;
  123.   struct diskio dio;
  124.  
  125.   dprintf("write_device(%d,%p,%u,%u)\n", drive, buf, nsecs, sector);
  126.  
  127.   dio.startsector = sector;
  128.   dio.sectors     = nsecs;
  129.   dio.bufoffs     = (uintptr_t)buf;
  130.   asm("movw %%ds,%0" : "=m" (dio.bufseg));
  131.  
  132.   asm volatile("int $0x26 ; setc %0 ; popfw"
  133.            : "=abcdm" (err)
  134.            : "a" (drive-1), "b" (&dio), "c" (-1), "d" (buf));
  135.  
  136.   if ( err )
  137.     die("sector write error");
  138. }
  139.  
  140. void read_device(int drive, const void *buf, size_t nsecs, unsigned int sector)
  141. {
  142.   uint8_t err;
  143.   struct diskio dio;
  144.  
  145.   dprintf("read_device(%d,%p,%u,%u)\n", drive, buf, nsecs, sector);
  146.  
  147.   dio.startsector = sector;
  148.   dio.sectors     = nsecs;
  149.   dio.bufoffs     = (uintptr_t)buf;
  150.   asm("movw %%ds,%0" : "=m" (dio.bufseg));
  151.  
  152.   asm volatile("int $0x25 ; setc %0 ; popfw"
  153.            : "=abcdm" (err)
  154.            : "a" (drive-1), "b" (&dio), "c" (-1), "d" (buf));
  155.  
  156.   if ( err )
  157.     die("sector read error");
  158. }  
  159.  
  160. /* Both traditional DOS and FAT32 DOS return this structure, but
  161.    FAT32 return a lot more data, so make sure we have plenty of space */
  162. struct deviceparams {
  163.   uint8_t specfunc;
  164.   uint8_t devtype;
  165.   uint16_t devattr;
  166.   uint16_t cylinders;
  167.   uint8_t mediatype;
  168.   uint16_t bytespersec;
  169.   uint8_t secperclust;
  170.   uint16_t ressectors;
  171.   uint8_t fats;
  172.   uint16_t rootdirents;
  173.   uint16_t sectors;
  174.   uint8_t media;
  175.   uint16_t fatsecs;
  176.   uint16_t secpertrack;
  177.   uint16_t heads;
  178.   uint32_t hiddensecs;
  179.   uint32_t hugesectors;
  180.   uint8_t lotsofpadding[224];
  181. } __attribute__((packed));
  182.  
  183. uint32_t get_partition_offset(int drive)
  184. {
  185.   uint8_t err;
  186.   uint16_t rv;
  187.   struct deviceparams dp;
  188.  
  189.   dp.specfunc = 1;        /* Get current information */
  190.   
  191.   rv = 0x440d;
  192.   asm volatile("int $0x21 ; setc %0"
  193.            : "=abcdm" (err), "+a" (rv)
  194.            : "b" (drive), "c" (0x0860), "d" (&dp));
  195.  
  196.   if ( !err )
  197.     return dp.hiddensecs;
  198.     
  199.   rv = 0x440d;
  200.   asm volatile("int $0x21 ; setc %0"
  201.            : "=abcdm" (err), "+a" (rv)
  202.            : "b" (drive), "c" (0x4860), "d" (&dp));
  203.  
  204.   if ( !err )
  205.     return dp.hiddensecs;
  206.  
  207.   die("could not find partition start offset");
  208. }
  209.  
  210. struct rwblock {
  211.   uint8_t special;
  212.   uint16_t head;
  213.   uint16_t cylinder;
  214.   uint16_t firstsector;
  215.   uint16_t sectors;
  216.   uint16_t bufferoffset;
  217.   uint16_t bufferseg;
  218. } __attribute__((packed));
  219.  
  220. static struct rwblock mbr = {
  221.   .special = 0,
  222.   .head = 0,
  223.   .cylinder = 0,
  224.   .firstsector = 0,        /* MS-DOS, unlike the BIOS, zero-base sectors */
  225.   .sectors = 1,
  226.   .bufferoffset = 0,
  227.   .bufferseg = 0
  228. };
  229.  
  230. void write_mbr(int drive, const void *buf)
  231. {
  232.   uint16_t rv;
  233.   uint8_t err;
  234.  
  235.   dprintf("write_mbr(%d,%p)\n", drive, buf);
  236.  
  237.   mbr.bufferoffset = (uintptr_t)buf;
  238.   asm("movw %%ds,%0" : "=m" (mbr.bufferseg));
  239.  
  240.   rv = 0x440d;
  241.   asm volatile("int $0x21 ; setc %0"
  242.            : "=abcdm" (err), "+a" (rv)
  243.            : "c" (0x0841), "d" (&mbr), "b" (drive));
  244.  
  245.   if ( !err )
  246.     return;
  247.  
  248.   rv = 0x440d;
  249.   asm volatile("int $0x21 ; setc %0"
  250.            : "=abcdm" (err), "+a" (rv)
  251.            : "c" (0x4841), "d" (&mbr), "b" (drive));
  252.   
  253.   if ( err )
  254.     die("mbr write error");
  255. }
  256.  
  257. void read_mbr(int drive, const void *buf)
  258. {
  259.   uint16_t rv;
  260.   uint8_t err;
  261.  
  262.   dprintf("read_mbr(%d,%p)\n", drive, buf);
  263.  
  264.   mbr.bufferoffset = (uintptr_t)buf;
  265.   asm("movw %%ds,%0" : "=m" (mbr.bufferseg));
  266.  
  267.   rv = 0x440d;
  268.   asm volatile("int $0x21 ; setc %0"
  269.            : "=abcdm" (err), "+a" (rv)
  270.            : "c" (0x0861), "d" (&mbr), "b" (drive));
  271.  
  272.   if ( !err )
  273.     return;
  274.  
  275.   rv = 0x440d;
  276.   asm volatile("int $0x21 ; setc %0"
  277.            : "=abcdm" (err), "+a" (rv)
  278.            : "c" (0x4861), "d" (&mbr), "b" (drive));
  279.   
  280.   if ( err )
  281.     die("mbr read error");
  282. }
  283.  
  284. /* This call can legitimately fail, and we don't care, so ignore error return */
  285. void set_attributes(const char *file, int attributes)
  286. {
  287.   uint16_t rv = 0x4301;
  288.  
  289.   dprintf("set_attributes(\"%s\", 0x%02x)\n", file, attributes);
  290.  
  291.   asm volatile("int $0x21"
  292.            : "+a" (rv)
  293.            : "c" (attributes), "d" (file));
  294. }
  295.  
  296. /*
  297.  * Version of the read_device function suitable for libfat
  298.  */
  299. int libfat_xpread(intptr_t pp, void *buf, size_t secsize, libfat_sector_t sector)
  300. {
  301.   read_device(pp, buf, 1, sector);
  302.   return secsize;
  303. }
  304.  
  305. static inline void get_dos_version(void)
  306. {
  307.   uint16_t ver = 0x3001;
  308.   asm("int $0x21 ; xchgb %%ah,%%al" : "+a" (ver) : : "ebx", "ecx");
  309.   dos_version = ver;
  310.   dprintf("DOS version %d.%d\n", (dos_version >> 8), dos_version & 0xff);
  311. }
  312.  
  313. /* The locking interface relies on static variables.  A massive hack :( */
  314. static uint16_t lock_level;
  315.  
  316. static inline void set_lock_device(uint8_t device)
  317. {
  318.   lock_level = device;
  319. }
  320.  
  321. void lock_device(int level)
  322. {
  323.   uint16_t rv;
  324.   uint8_t err;
  325.   uint16_t lock_call;
  326.  
  327.   if ( dos_version < 0x0700 )
  328.     return;            /* Win9x/NT only */
  329.  
  330. #if 0
  331.   /* DOS 7.10 = Win95 OSR2 = first version with FAT32 */
  332.   lock_call = (dos_version >= 0x0710) ? 0x484A : 0x084A;
  333. #else
  334.   lock_call = 0x084A;        /* MSDN says this is OK for all filesystems */
  335. #endif
  336.  
  337.   while ( (lock_level >> 8) < level ) {
  338.     uint16_t new_level = lock_level + 0x0100;
  339.     dprintf("Trying lock %04x...\n", new_level);
  340.     rv = 0x444d;
  341.     asm volatile("int $0x21 ; setc %0"
  342.          : "=abcdm" (err), "+a" (rv)
  343.          : "b" (new_level), "c" (lock_call), "d"(0x0001));
  344.     if ( err ) {
  345.       /* rv == 0x0001 means this call is not supported, if so we
  346.      assume locking isn't needed (e.g. Win9x in DOS-only mode) */
  347.       if ( rv == 0x0001 )
  348.     return;
  349.       else
  350.     die("could not lock device");
  351.     }
  352.  
  353.     lock_level = new_level;
  354.   }
  355.   return;
  356. }
  357.  
  358. void unlock_device(int level)
  359. {
  360.   uint16_t rv;
  361.   uint8_t err;
  362.   uint16_t unlock_call;
  363.  
  364.   if ( dos_version < 0x0700 )
  365.     return;            /* Win9x/NT only */
  366.  
  367. #if 0
  368.   /* DOS 7.10 = Win95 OSR2 = first version with FAT32 */
  369.   unlock_call = (dos_version >= 0x0710) ? 0x486A : 0x086A;
  370. #else
  371.   unlock_call = 0x086A;        /* MSDN says this is OK for all filesystems */
  372. #endif
  373.  
  374.   while ( (lock_level >> 8) > level ) {
  375.     uint16_t new_level = lock_level - 0x0100;
  376.     rv = 0x440d;
  377.     asm volatile("int $0x21 ; setc %0"
  378.          : "=abcdm" (err), "+a" (rv)
  379.          : "b" (new_level), "c" (unlock_call));
  380.     lock_level = new_level;
  381.   }
  382. }
  383.  
  384.  
  385. /*
  386.  * This function does any desired MBR manipulation; called with the device lock held.
  387.  */
  388. struct mbr_entry {
  389.   uint8_t active;        /* Active flag */
  390.   uint8_t bhead;        /* Begin head */
  391.   uint8_t bsector;        /* Begin sector */
  392.   uint8_t bcylinder;        /* Begin cylinder */
  393.   uint8_t filesystem;        /* Filesystem value */
  394.   uint8_t ehead;        /* End head */
  395.   uint8_t esector;        /* End sector */
  396.   uint8_t ecylinder;        /* End cylinder */
  397.   uint32_t startlba;        /* Start sector LBA */
  398.   uint32_t sectors;        /* Length in sectors */
  399. } __attribute__((packed));
  400.  
  401. static void adjust_mbr(int device, int writembr, int set_active)
  402. {
  403.   static unsigned char sectbuf[512];
  404.   int i;
  405.  
  406.   if ( !writembr && !set_active )
  407.     return;            /* Nothing to do */
  408.  
  409.   read_mbr(device, sectbuf);
  410.  
  411.   if ( writembr ) {
  412.     memcpy(sectbuf, syslinux_mbr, syslinux_mbr_len);
  413.     *(uint16_t *)(sectbuf+510) = 0xaa55;
  414.   }
  415.     
  416.   if ( set_active ) {
  417.     uint32_t offset = get_partition_offset(device);
  418.     struct mbr_entry *me = (struct mbr_entry *)(sectbuf+446);
  419.     int found = 0;
  420.     
  421.     for ( i = 0 ; i < 4 ; i++ ) {
  422.       if ( me->startlba == offset ) {
  423.     me->active = 0x80;
  424.     found++;
  425.       } else {
  426.     me->active = 0;
  427.       }
  428.       me++;
  429.     }
  430.  
  431.     if ( found < 1 ) {
  432.       die("partition not found");
  433.     } else if ( found > 1 ) {
  434.       die("multiple aliased partitions found");
  435.     }
  436.   }
  437.     
  438.   write_mbr(device, sectbuf);
  439. }
  440.  
  441. int main(int argc, char *argv[])
  442. {
  443.   static unsigned char sectbuf[512];
  444.   int dev_fd, fd;
  445.   static char ldlinux_name[] = "@:\\LDLINUX.SYS";
  446.   char **argp, *opt;
  447.   int force = 0;        /* -f (force) option */
  448.   struct libfat_filesystem *fs;
  449.   libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
  450.   int32_t ldlinux_cluster;
  451.   int nsectors;
  452.   const char *device = NULL, *bootsecfile = NULL;
  453.   const char *errmsg;
  454.   int i;
  455.   int writembr = 0;        /* -m (write MBR) option */
  456.   int set_active = 0;        /* -a (set partition active) option */
  457.  
  458.   dprintf("argv = %p\n", argv);
  459.   for ( i = 0 ; i <= argc ; i++ )
  460.     dprintf("argv[%d] = %p = \"%s\"\n", i, argv[i], argv[i]);
  461.  
  462.   (void)argc;            /* Unused */
  463.   
  464.   get_dos_version();
  465.  
  466.   for ( argp = argv+1 ; *argp ; argp++ ) {
  467.     if ( **argp == '-' ) {
  468.       opt = *argp + 1;
  469.       if ( !*opt )
  470.     usage();
  471.  
  472.       while ( *opt ) {
  473.     switch ( *opt ) {
  474.     case 's':        /* Use "safe, slow and stupid" code */
  475.       syslinux_make_stupid();
  476.       break;
  477.     case 'f':        /* Force install */
  478.       force = 1;
  479.       break;
  480.     case 'm':        /* Write MBR */
  481.       writembr = 1;
  482.       break;
  483.     case 'a':        /* Set partition active */
  484.       set_active = 1;
  485.       break;
  486.     default:
  487.       usage();
  488.     }
  489.     opt++;
  490.       }
  491.     } else {
  492.       if ( bootsecfile )
  493.     usage();
  494.       else if ( device )
  495.     bootsecfile = *argp;
  496.       else
  497.       device = *argp;
  498.     }
  499.   }
  500.  
  501.   if ( !device )
  502.     usage();
  503.  
  504.   /*
  505.    * Figure out which drive we're talking to
  506.    */
  507.   dev_fd = (device[0] & ~0x20) - 0x40;
  508.   if ( dev_fd < 1 || dev_fd > 26 || device[1] != ':' || device[2] )
  509.     usage();
  510.  
  511.   set_lock_device(dev_fd);
  512.  
  513.   lock_device(2);        /* Make sure we can lock the device */
  514.   read_device(dev_fd, sectbuf, 1, 0);
  515.   unlock_device(1);
  516.  
  517.   /*
  518.    * Check to see that what we got was indeed an MS-DOS boot sector/superblock
  519.    */
  520.   if( (errmsg = syslinux_check_bootsect(sectbuf)) ) {
  521.     unlock_device(0);
  522.     puts(errmsg);
  523.     putchar('\n');
  524.     exit(1);
  525.   }
  526.   
  527.   ldlinux_name[0] = dev_fd | 0x40;
  528.  
  529.   set_attributes(ldlinux_name, 0);
  530.   fd = creat(ldlinux_name, 0x07); /* SYSTEM HIDDEN READONLY */
  531.   write_file(fd, syslinux_ldlinux, syslinux_ldlinux_len);
  532.   close(fd);
  533.  
  534.   /*
  535.    * Now, use libfat to create a block map.  This probably
  536.    * should be changed to use ioctl(...,FIBMAP,...) since
  537.    * this is supposed to be a simple, privileged version
  538.    * of the installer.
  539.    */
  540.   lock_device(2);
  541.   fs = libfat_open(libfat_xpread, dev_fd);
  542.   ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
  543.   secp = sectors;
  544.   nsectors = 0;
  545.   s = libfat_clustertosector(fs, ldlinux_cluster);
  546.   while ( s && nsectors < 65 ) {
  547.     *secp++ = s;
  548.     nsectors++;
  549.     s = libfat_nextsector(fs, s);
  550.   }
  551.   libfat_close(fs);
  552.  
  553.   /*
  554.    * Patch ldlinux.sys and the boot sector
  555.    */
  556.   syslinux_patch(sectors, nsectors);
  557.  
  558.   /*
  559.    * Write the now-patched first sector of ldlinux.sys
  560.    */
  561.   lock_device(3);
  562.   write_device(dev_fd, syslinux_ldlinux, 1, sectors[0]);
  563.  
  564.   /*
  565.    * Muck with the MBR, if desired, while we hold the lock
  566.    */
  567.   adjust_mbr(dev_fd, writembr, set_active);
  568.  
  569.   /*
  570.    * To finish up, write the boot sector
  571.    */
  572.  
  573.   /* Read the superblock again since it might have changed while mounted */
  574.   read_device(dev_fd, sectbuf, 1, 0);
  575.  
  576.   /* Copy the syslinux code into the boot sector */
  577.   syslinux_make_bootsect(sectbuf);
  578.  
  579.   /* Write new boot sector */
  580.   if ( bootsecfile ) {
  581.     unlock_device(0);
  582.     fd = creat(bootsecfile, 0x20); /* ARCHIVE */
  583.     write_file(fd, sectbuf, 512);
  584.     close(fd);
  585.   } else {
  586.     write_device(dev_fd, sectbuf, 1, 0);
  587.     unlock_device(0);
  588.   }
  589.  
  590.   /* Done! */
  591.  
  592.   return 0;
  593. }
  594.  
  595.